﻿/*
Copyright © 2011 François Karman

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

See <license.txt> for the complete license of the software.
*/

using System;
using System.Collections.Generic;

namespace Simple.Storage
{
    /// <summary>
    /// The entry point of the storage system.
    /// </summary>
    public class Engine
    {
        /// <summary>
        /// The unique instance of the storage engine.
        /// </summary>
        private static readonly Engine instance = new Engine();

        /// <summary>
        /// The registered data sources.
        /// </summary>
        private readonly IDictionary<string, IDataSource> datasources;

        /// <summary>
        /// The initializer used when creating new <see cref="IDataSource"/>.
        /// </summary>
        private IInitializer initializer;

        /// <summary>
        /// Prevents a default instance of the Engine class from being created.
        /// </summary>
        private Engine()
        {
            this.datasources = new Dictionary<string, IDataSource>();
            this.initializer = new Api.DefaultInitializer();
        }

        /// <summary>
        /// Gets the unique instance of the storage engine.
        /// </summary>
        public static Engine Instance
        {
            get { return instance; }
        }

        /// <summary>
        /// Gets or sets the initializer used when creating new <see cref="IDataSource"/>.
        /// </summary>
        /// <exception cref="ArgumentNullException">
        /// The value provided to the setter is <c>null</c>.
        /// </exception>
        public IInitializer Initializer
        {
            get
            {
                return this.initializer;
            }

            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value");
                }

                this.initializer = value;
            }
        }

        /// <summary>
        /// Registers a new data source.
        /// </summary>
        /// <param name="definition">
        /// The data source definition used to create the data source.
        /// </param>
        /// <returns>
        /// The created data source.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="definition"/> parameter is <c>null</c>.
        /// </exception>
        /// <exception cref="NotSupportedException">
        /// The data source is already registered (based on its name).
        /// </exception>
        /// <exception cref="DefinitionException">
        /// The provided definition is not correct.
        /// </exception>
        public IDataSource RegisterDataSource(IDataSourceDefinition definition)
        {
            if (definition == null)
            {
                throw new ArgumentNullException("definition");
            }

            if (this.datasources.ContainsKey(definition.Name))
            {
                // TODO: Add a message
                throw new NotSupportedException();
            }

            Api.DataSource datasource = new Api.DataSource(definition);
            datasource.Initialize(this.Initializer);

            this.datasources.Add(definition.Name, datasource);
            return datasource;
        }

        /// <summary>
        /// Unregisters a data source.
        /// </summary>
        /// <param name="name">The name of the data source.</param>
        /// <remarks>
        /// This method does nothing if the data source doesn't exist or is already
        /// unregistered.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="name"/> parameter is <c>null</c> or empty.
        /// </exception>
        public void UnregisterDataSource(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            if (this.datasources.ContainsKey(name))
            {
                this.datasources.Remove(name);
            }
        }

        /// <summary>
        /// Retrieves a registered data source based on its unique name.
        /// </summary>
        /// <param name="name">The name of the data source.</param>
        /// <returns>The retrieved data source.</returns>
        public IDataSource RetrieveDataSource(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            return this.datasources[name];
        }
    }
}
